﻿using System;
using System.Linq;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;
using Wolfpack.Core.Interfaces;
using Wolfpack.Core.Interfaces.Entities;

namespace Wolfpack.Contrib.Publishers.MongoDb
{
    public class MongoDbRepository : INotificationRepository
    {
        private readonly MongoDbConfiguration _config;
        private MongoDatabase _database;

        private MongoCollection<NotificationEvent> NotificationEventCollection
        {
            get
            {
                if (_database == null)
                    throw new InvalidOperationException("Not connected to MongoDb server/database!");
                return _database.GetCollection<NotificationEvent>(_config.CollectionName);
            }
        }

        public MongoDbRepository(MongoDbConfiguration config)
        {
            _config = config;
        }

        public void Initialise()
        {      
            var client = ConnectViaClient();
            var server = client.GetServer();
            _database = server.GetDatabase(_config.DatabaseName);

            if (!_database.CollectionExists(_config.CollectionName))
            {
                var options = new CollectionOptionsBuilder().SetAutoIndexId(_config.AutoIndexId)
                    .SetCapped(_config.Capped);

                if (_config.Max.HasValue)
                    options.SetMaxSize(_config.Max.Value);
                if (_config.Size.HasValue)
                    options.SetMaxSize(_config.Size.Value);

                var result = _database.CreateCollection(_config.CollectionName, options);

                if (!result.Ok)
                    throw new InvalidOperationException(string.Format("Unable to create collection '{0}'; Message:='{1}'", 
                        _config.CollectionName,
                        result.ErrorMessage ?? "None Provided"));
            }

            NotificationEventCollection.EnsureIndex(new IndexKeysBuilder().Ascending("EventType"));
            NotificationEventCollection.EnsureIndex(new IndexKeysBuilder().Ascending("Result"));
            NotificationEventCollection.EnsureIndex(new IndexKeysBuilder().Ascending("GeneratedOnUtc"));
            NotificationEventCollection.EnsureIndex(new IndexKeysBuilder().Ascending("State"));
            NotificationEventCollection.EnsureIndex(new IndexKeysBuilder().Ascending("CheckId"));
        }
            
        public IQueryable<NotificationEvent> Filter(params INotificationRepositoryQuery[] filters)
        {
            return filters.Aggregate(NotificationEventCollection.AsQueryable(), (current, filter) => filter.Filter(current));
        }

        public bool GetById(Guid id, out NotificationEvent notification)
        {
            notification = _database.GetCollection<NotificationEvent>(_config.CollectionName)
                .AsQueryable()
                .SingleOrDefault(n => n.Id.Equals(id));
            return notification != null;
        }

        public IQueryable<NotificationEvent> GetByState(MessageStateTypes state)
        {
            return _database.GetCollection<NotificationEvent>(_config.CollectionName)
                .AsQueryable()
                .Where(n => n.State.Equals(state));
        }

        public void Add(NotificationEvent notification)
        {
            _database.GetCollection<NotificationEvent>(_config.CollectionName)
                .Save(notification);
        }

        public void Delete(Guid notificationId)
        {
            _database.GetCollection<NotificationEvent>(_config.CollectionName)
                .Remove(Query<NotificationEvent>.EQ(n => n.Id, notificationId));
        }

        private MongoClient ConnectViaClient()
        {
            var connectionString = string.Format("mongodb://{0}:{1}", _config.ServerName, _config.Port);
            return new MongoClient(connectionString);
        }
    }
}